﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Drawing;
using Microsoft.DirectX;

namespace NGE
{
    [Serializable]
    public struct Datagram
    {
        
        [Serializable]
        public struct PlayerCharacterSceneInfo
        {
            public string Name;
            public string InSceneName;
            public Vector3 Location;
            public Direction Direction;
        }
        [Serializable]
        public struct PlayerCharacterBattleInfo
        {
            public string Name;
            public int BattleAct;
            public Vector3 TargetLocation;
        }
        [Serializable]
        public struct PlayerCharacterMoveInfo
        {
            public string Name;
            public Direction Direction;
            public float MoveSpeed;
            public int Steps;
        }
        [Serializable]
        public struct PlayerCharacterSayInfo
        {
            public string Name;
            public string Said;
        }
    }
    [Serializable]
    public struct PlayerCharacterActs
    {
        public const int TEST = 0x300;

        public const int GETPC = 0;
        public const int GETScene = 1;
        public const int GETOPC = 2;

        public const int SETPC = 0x100;
        public const int SETScene = 0x101;
        public const int SETOPC = 0x102;

        public const int ChangeDirection = 0x200;
        public const int StartMove = 0x201;
        public const int StopMove = 0x202;
        public const int Say = 0x203;
        public const int EnterMetal = 0x204;
        public const int ExitMetal = 0x205;
        public const int EnterScene = 0x206;
        public const int ExitScene = 0x207;
        public const int BattleMoveCommanding = 0x208;
        public const int BattleAttackCommanding = 0x209;
        public const int BattleItemCommanding = 0x210;
    }

    [Serializable]
    public class PlayerCharacterEventArgs : EventArgs
    {
        public PlayerCharacterEventArgs(int act, object arg)
        {
            Act = act;
            Arg = arg;
        }

        public int Act;
        public object Arg;

    }
    public delegate void PlayerCharacterEvent(object sender, PlayerCharacterEventArgs ea);
    [Serializable]
    public class Character : Sprite
    {
        public Character()
            : base()
        {
            inMetalIndex = -1;

            Scrpit = "";
            Type = SpriteType.NPC;
            DoorState = DoorState.Closed;
            DefaultDoorState = DoorState.Closed;
        }
        //[NonSerialized]
        public int Handle;
        public string Said = "";
        public int SaidDuration = 5;
        [NonSerialized]
        public double SaidStartTime;

        //public double Delay = 5.0;
        //public double LastActTime;
        public bool IsWaitingCommand;

        event PlayerCharacterEvent OnPlayerCharacterAct;        
        List<Metal> metals = new List<Metal>();
        int inMetalIndex;
        int searchMetal(Metal metal)
        {
            int i = 0;
            foreach (Metal mtl in metals)
            {
                if (metal.GUID == mtl.GUID)
                {
                    return i;
                }
                i++;
            }
            return -1;
        }

        public Datagram.PlayerCharacterSceneInfo SceneInfo
        {
            get
            {
                Datagram.PlayerCharacterSceneInfo pcsi;
                pcsi.Direction = Direction;
                pcsi.Location = Location;
                pcsi.Name = Name;
                pcsi.InSceneName = InSceneName;
                return pcsi;
            }
            set
            {
                if (Name == value.Name)
                {
                    Direction = value.Direction;
                    Location = value.Location;
                    InSceneName = value.InSceneName;
                }
            }
        }

        //public Datagram.PlayerCharacterMoveInfo MoveInfo
        //{            
        //    set
        //    {
        //        if (Name == value.Name)
        //        {
        //            cmdMoveEnqueue(value);
        //        }
        //    }
        //}
        public Datagram.PlayerCharacterBattleInfo BattleInfo
        {
            get
            {
                Datagram.PlayerCharacterBattleInfo pcbi;
                pcbi.Name = Name;
                pcbi.BattleAct = BattleAct;
                pcbi.TargetLocation = TargetLocation;
                return pcbi;
            }
            set
            {
                if (Name == value.Name)
                {
                    BattleAct = value.BattleAct;
                    TargetLocation = value.TargetLocation;
                }
            }
        }
        public Datagram.PlayerCharacterSayInfo SayInfo
        {
            get
            {
                Datagram.PlayerCharacterSayInfo pcsay;
                pcsay.Name = Name;
                pcsay.Said = Said;
                return pcsay;
            }
            set
            {
                if (Name == value.Name)
                {
                    Said = value.Said;
                }
            }
        }

        public int InMetalIndex
        {
            get
            {
                return inMetalIndex;
            }
        }
        public int MetalCount
        {
            get
            {
                return metals.Count;
            }
        }
        public Metal DrivingMetal
        {
            get
            {
                if (inMetalIndex < 0)
                {
                    return null;
                }
                return metals[inMetalIndex];
            }
        }
        public void GotMetal(Metal mtl)
        {
            metals.Add(mtl);
        }
        public void EnterMetal(Metal mtl)
        {
            int i = searchMetal(mtl);
            EnterMetal(i);
        }
        public void EnterMetal(int i)
        {
            if (NeedMove>0)
            {
                return;
            }
            inMetalIndex = i;
            metals[inMetalIndex].IsRunning = true;
            Location = metals[inMetalIndex].StopLocation;
            Direction = metals[inMetalIndex].StopDirection;
        }
        public void ExitMetal()
        {
            if (NeedMove>0)
            {
                return;
            }
            if (inMetalIndex == -1)
            {
                return;
            }
            metals[inMetalIndex].StopDirection = Direction;
            metals[inMetalIndex].StopLocation = Location;
            metals[inMetalIndex].IsRunning = false;
            inMetalIndex = -1;
        }
        public Metal GetMetal(int i)
        {
            return metals[i];
        }
        public Vector3 GetNextLocationRight(Size tileSize)
        {
            Vector3 loc = GetNextLocation(tileSize);
            loc.X += tileSize.Width;
            return loc;
        }
        
        public void UnmontEventHandler()
        {
            OnPlayerCharacterAct = null;
        }
        public void MountEventHandler(PlayerCharacterEvent pce)
        {
            UnmontEventHandler();
            OnPlayerCharacterAct = new PlayerCharacterEvent(pce);
        }
        public bool CanMove(Scene scn)
        {
            if (scn.TileCodeLayer[GetNextLocation(scn.TileSize)] != null)
            {
                if (!scn.TileCodeLayer[GetNextLocation(scn.TileSize)].CHR)
                {
                    return false;
                }
            }
            return true;
        }
        public float Move(Direction dir, int steps, Scene scn,List<Character> chrs)
        {
            Size tileSize = scn.TileSize;
            Direction = dir;
            if (OnPlayerCharacterAct != null)
            {
                Datagram.PlayerCharacterMoveInfo pcmi;
                pcmi.Name = Name;
                pcmi.MoveSpeed = 0;
                pcmi.Steps = 0;
                pcmi.Direction = Direction;
                OnPlayerCharacterAct(this, new PlayerCharacterEventArgs(PlayerCharacterActs.ChangeDirection, pcmi));
            }

            if (scn.TileCodeLayer[GetNextLocation(tileSize)] != null)
            {
                if (!scn.TileCodeLayer[GetNextLocation(tileSize)].CHR)
                {
                    return 0;
                }
            }
            if (chrs != null)
            {
                foreach (Character npc in scn.NPCs)
                {
                    if (npc.Type == SpriteType.NPC)
                    {
                        if (GetNextLocation(tileSize).Equals(npc.Location))
                        {
                            return 0;
                        }
                    }
                    else if (npc.Type == SpriteType.Box)
                    {
                        if (GetNextLocation(tileSize).Equals(npc.Location))
                        {
                            return 0;
                        }
                    }
                    else if (npc.Type == SpriteType.Door)
                    {
                        if (GetNextLocation(tileSize).Equals(npc.Location))
                        {
                            if (npc.DoorState == DoorState.Locked)
                            {
                                return 0;
                            }
                        }
                    }
                }
            }
            if (InMetalIndex > -1)
            {
                if (scn.TileCodeLayer[GetNextLocationRight(tileSize)] != null)
                {
                    if (!scn.TileCodeLayer[GetNextLocationRight(tileSize)].CHR)
                    {
                        return 0;
                    }
                }
                if (scn.NPCs != null)
                {
                    foreach (Character npc in scn.NPCs)
                    {
                        if (npc.Type == SpriteType.NPC)
                        {
                            if (GetNextLocationRight(tileSize).Equals(npc.Location))
                            {
                                return 0;
                            }
                        }
                    }
                }
            }

            needMoveStep = steps;
            if (Direction == Direction.U || Direction == Direction.D)
            {
                {
                    needMovePixel = tileSize.Height * needMoveStep;
                }
            }
            else if (Direction == Direction.L || Direction == Direction.R)
            {
                {
                    needMovePixel = tileSize.Width * needMoveStep;
                }
            }
            LastLocation = Location;

            float ms = moveSpeed;
            if (InMetalIndex > -1)
            {
                ms = DrivingMetal.MoveSpeed;
            }

            if (OnPlayerCharacterAct != null)
            {
                Datagram.PlayerCharacterMoveInfo pcmi;
                pcmi.Name = Name;
                pcmi.MoveSpeed = ms;
                pcmi.Steps = steps;
                pcmi.Direction = Direction;
                OnPlayerCharacterAct(this, new PlayerCharacterEventArgs(PlayerCharacterActs.StartMove, pcmi));
            }
            return ms;
        }
        public void Heartbeat(Game game)
        {
            if (Type == SpriteType.Door)
            {
                if (game.PC == null)
                {
                    return;
                }
                if (canOpenDoor(game.CHRs, game.SCN.TileSize))
                {
                    DoorState = DoorState.Open;
                }
                else
                {
                    RestoreDoorState();
                }
            }
            else if (Type == SpriteType.PC || Type == SpriteType.OPC || Type == SpriteType.NPC)
            {
                bool check = false;
                if (needMovePixel > 0 && needMovePixel <= moveSpeed)
                {
                    check = true;
                }
                Heartbeat();
                if (check)
                {
                    if (Type == SpriteType.PC)
                    {
                        if (OnPlayerCharacterAct != null)
                        {
                            OnPlayerCharacterAct(this, new PlayerCharacterEventArgs(PlayerCharacterActs.StopMove, SceneInfo));
                        }
                        EntryCode ec = game.SCN.IsAtJumpPoint(Location);
                        if (ec != null)
                        {
                            Datagram.PlayerCharacterSceneInfo pcsi;
                            pcsi.Name = Name;
                            pcsi.InSceneName = ec.ToSceneFileName;
                            pcsi.Location = ec.DefaultLocation;
                            pcsi.Direction = ec.DefaultDirection;
                            PlayerCharacterEventArgs pcea = new PlayerCharacterEventArgs(PlayerCharacterActs.EnterScene, pcsi);
                            if (OnPlayerCharacterAct != null)
                            {
                                OnPlayerCharacterAct(this, pcea);
                            }
                        }
                    }
                    else
                    {
                        EntryCode ec = game.SCN.IsAtJumpPoint(Location);
                        if (ec != null)
                        {
                            InSceneName = ec.ToSceneName;
                        }
                    }
                }
                if (NeedMove == 0)
                {
                    if (cmdMoveQue == null)
                    {
                        cmdMoveQue = new Queue<Datagram.PlayerCharacterMoveInfo>();
                    }
                    if (cmdMoveQue.Count > 0)
                    {
                        Datagram.PlayerCharacterMoveInfo pcmi = cmdMoveQue.Dequeue();
                        float ms = Move(pcmi.Direction, pcmi.Steps, game.SCN,game.CHRs);
                        if (Type == SpriteType.PC)
                        {
                            game.SCN.Move(pcmi.Direction, pcmi.Steps, ms);
                        }
                    }
                }
            }
        }
        protected void Heartbeat()
        {
            if (needMovePixel > 0)
            {
                float ms = moveSpeed;
                if (InMetalIndex > -1)
                {
                    ms = DrivingMetal.MoveSpeed;
                }
                if (Direction == Direction.U)
                {
                    Location.Y -= ms;
                }
                else if (Direction == Direction.L)
                {
                    Location.X -= ms;
                }
                else if (Direction == Direction.D)
                {
                    Location.Y += ms;
                }
                else if (Direction == Direction.R)
                {
                    Location.X += ms;
                }
                if (needMovePixel <= ms)
                {
                    needMovePixel = 0;

                    //if (cmdMoveQue.Count > 0)
                    //{
                    //    Datagram.PlayerCharacterSceneInfo csi = cmdMoveQue.Dequeue();
                    //    if ((int)csi.Location.X > (int)Location.X)
                    //    {
                    //        int step = (int)(TargetLocation.X - Location.X) / scn.TileSize.Width;
                    //        Move(Direction.R, step, scn);
                    //    }
                    //    else if ((int)csi.Location.X < (int)Location.X)
                    //    {
                    //        int step = -(int)(csi.Location.X - Location.X) / scn.TileSize.Width;
                    //        Move(Direction.L, step, scn);
                    //    }
                    //    if ((int)TargetLocation.Y > (int)Location.Y)
                    //    {
                    //        int step = (int)(csi.Location.Y - Location.Y) / scn.TileSize.Height;
                    //        Move(Direction.D, step, scn);
                    //    }
                    //    else if ((int)TargetLocation.Y < (int)Location.Y)
                    //    {
                    //        int step = -(int)(csi.Location.Y - Location.Y) / scn.TileSize.Height;
                    //        Move(Direction.U, step, scn);
                    //    }
                    //}


                }
                else
                {
                    needMovePixel -= ms;
                }
            }
        }

        #region bag
        List<Prop> Bag;

        public void BagIN(Prop obj)
        {
            if (Bag == null)
            {
                Bag = new List<Prop>();
            }
            Bag.Insert(0, obj);
        }
        
        public void BagOUT(int i)
        {
            if (i < 0)
            {
                return;
            }
            if (Bag == null)
            {
                Bag = new List<Prop>();
                return;
            }
            Bag.RemoveAt(i);
        }
        public void BagOUT(Guid guid)
        {
            int i = getPropIndex(guid);
            BagOUT(i);
        }

        public Prop BagUSE(int i)
        {
            if (Bag == null)
            {
                Bag = new List<Prop>();
                return null;
            }
            if (Bag.Count == 0)
            {
                return null;
            }
            return Bag[i];
        }
        public Prop BagUSE(Guid guid)
        {
            int i = getPropIndex(guid);
            if (i > -1)
            {
                return BagUSE(i);
            }
            return null;
        }

        int getPropIndex(Guid guid)
        {
            for (int i = 0; i < Bag.Count; i++)
            {
                if(Bag[i].GUID.Equals(guid))
                {
                    return i;
                }
            }
            return -1;
        }
        #endregion

        #region for fight
        public int MoveRange = 3;
        public List<Vector3> CanMoveZone
        {
            get
            {
                return getZone(MoveRange);
            }
        }
        public bool CanMove(Vector3 loc)
        {
            foreach (Vector3 v3 in CanMoveZone)
            {
                if (v3.Equals(loc))
                {
                    return true;
                }
            }
            return false;
        }

        public int AttackRange = 2;
        public List<Vector3> CanAttackZone
        {
            get
            {
                return getZone(AttackRange);
            }
        }
        public bool CanAttack(Vector3 loc)
        {
            foreach (Vector3 v3 in CanAttackZone)
            {
                if (v3.Equals(loc))
                {
                    return true;
                }
            }
            return false;
        }

        protected List<Vector3> getZone(int range)
        {
            List<Vector3> rz = new List<Vector3>();
            int ymax = range * 2 + 1;
            for (int y = 0; y < ymax / 2 + 1; y++)
            {
                int xmax = y * 2 + 1;
                for (int x = 0; x < xmax; x++)
                {
                    Vector3 v3 = new Vector3(x - xmax / 2, y - ymax / 2, 0);
                    rz.Add(v3);
                }
            }
            int rzCount = rz.Count;
            for (int i = 0; i < rzCount; i++)
            {
                Vector3 v3 = rz[i];
                if (v3.Y != 0)
                {
                    v3.Y = -v3.Y;
                    rz.Add(v3);
                }
            }
            rz.Remove(new Vector3());
            return rz;
        }

        protected Queue<Datagram.PlayerCharacterMoveInfo> cmdMoveQue;
        protected void cmdMoveEnqueue(Datagram.PlayerCharacterMoveInfo cmi)
        {
            if (cmdMoveQue == null)
            {
                cmdMoveQue = new Queue<Datagram.PlayerCharacterMoveInfo>();
            }
            cmdMoveQue.Enqueue(cmi);
        }
        public void Move(Datagram.PlayerCharacterMoveInfo cmi)
        {
            cmdMoveEnqueue(cmi);
        }
        public void ClearCommandQueue()
        {
            if (cmdMoveQue == null)
            {
                return;
            }
            cmdMoveQue.Clear();
        }

        public int BattleAct;

        #endregion

        #region for npc
        public SpriteType Type;
        public string Scrpit;
        public Vector3 DefaultLocation;
        public Direction DefaultDirection;
        public DoorState DefaultDoorState;
        public bool IsEnemy;

        [NonSerialized]
        public DoorState DoorState;

        public bool IsInDoorZone(Microsoft.DirectX.Vector3 loc, System.Drawing.Size tileSize)
        {
            loc.X /= tileSize.Width;
            loc.Y /= tileSize.Height;


            Vector3 lloc = Location;
            lloc.X /= tileSize.Width;
            lloc.Y /= tileSize.Height;
            if (loc.Equals(lloc))
            {
                return true;
            }
            if (loc.X == lloc.X)
            {
                if (loc.Y == lloc.Y - (AvatarSize.Height / tileSize.Height - 1) * tileSize.Height)
                {
                    return true;
                }
            }
            return false;
        }

        public void RestoreDoorState()
        {
            DoorState = DefaultDoorState;
        }
        public void RestoreDirection()
        {
            Direction = DefaultDirection;
        }

        bool canOpenDoor(List<Character> chrs, System.Drawing.Size tileSize)
        {
            foreach (Character pc in chrs)
            {
                if (canOpenDoor(pc, tileSize))
                {
                    return true;
                }
            }
            return false;
        }
        //bool canOpenDoor(List<NonPlayerCharacter> chrs, System.Drawing.Size tileSize)
        //{
        //    foreach (NonPlayerCharacter pc in chrs)
        //    {
        //        if (pc.Type == SpriteType.NPC)
        //            if (canOpenDoor(pc, tileSize))
        //            {
        //                return true;
        //            }
        //    }
        //    return false;
        //}
        bool canOpenDoor(Character chr, System.Drawing.Size tileSize)
        {
            if (chr.Type == SpriteType.Door)
            {
                return false;
            }
            if (chr.NeedMove > 0 && IsInDoorZone(chr.GetNextLocation(tileSize), tileSize))
            {
                return true;
            }
            else if (IsInDoorZone(chr.Location, tileSize))
            {
                return true;
            }
            else if (chr.NeedMove > 0 && chr.Direction == Direction.D && IsInDoorZone(chr.LastLocation, tileSize))
            {
                return true;
            }
            return false;
        }

        #endregion
    }
}
